

/// <reference path="./Maths.ts" />
namespace apeng {


	/**
	 * 数组的二次封装
	 * 内置去重处理
	 * @export
	 * @class Sets
	 * @template T
	 */
	export class Sets<T> {

		public static forEach<T>(arr: T[] | readonly T[], cb: (value: T, index: number) => (boolean | void), isLast: boolean = false) {
			if (isLast) {
				for (let i = arr.length - 1; i >= 0; i--) {
					if (cb(arr[i], i))
						break
				}
			}
			else {
				for (let i = 0; i < arr.length; i++) {
					if (cb(arr[i], i))
						break
				}
			}
		}

		/**
		 * 更新个数
		 * @param curCount 
		 * @param oldCount 
		 * @param itemCb 
		 */
		public static updateItemCount(curCount: number, oldCount: number, itemCb: (add: boolean) => void) {
			if (curCount < 0 || oldCount < 0)
				return
			if (curCount == oldCount)
				return
			let sub = curCount - oldCount
			if (sub > 0)
				for (let i = 0; i < sub; i++)
					itemCb(true)
			else
				for (let i = 0, length = Math.abs(sub); i < length; i++)
					itemCb(false)
		}

		public static pop<T>(arr: (T[]) | (readonly T[]), remove: boolean = false): T {
			if (remove)
				return (arr as any).pop() as T
			return arr[arr.length - 1]
		}

		public static delete<T>(arr: T[], value: T): boolean {
			if (value == undefined || value == null)
				return false
			if (arr.length == 0)
				return false
			let index = arr.indexOf(value)
			if (index == -1)
				return false
			arr.splice(index, 1)
			return true
		}

		/**
		 * 冒泡排序
		 * @param arr 
		 * @param cb 
		 */
		public static bubblingSort<T>(arr: T[], cb: (a: T, b: T) => boolean) {
			let size = arr.length
			for (let i = 0; i < size - 1; i++)
				for (let j = 0; j < size - 1 - i; j++) {
					if (cb(arr[j], arr[j + 1])) {
						let temp = arr[j]
						arr[j] = arr[j + 1]
						arr[j + 1] = temp
					}
				}
		}

		public static reverse<T>(arr: T[]) {
			for (var i = 0; i < arr.length / 2; i++) {
				var temp = arr[i]
				arr[i] = arr[arr.length - 1 - i]
				arr[arr.length - 1 - i] = temp
			}
			return arr
		}

		public static map<U, T>(arr: T[], cb: (value: T, index: number, array: T[]) => U): U[] {
			let set = []
			for (let i = 0; i < arr.length; i++) {
				let data = cb(arr[i], i, arr)
				set.push(data)
			}
			return set
		}

		public static has<T>(arr: T[] | undefined, value: T, stringCompare: boolean = false): boolean {
			if (!arr)
				return false
			if (arr.length == 0)
				return false

			let index: number

			if (stringCompare)
				index = this.map(arr, v => (v as any).toString()).indexOf((value as any).toString())
			else
				index = arr.indexOf(value)
			return index != -1
		}



		public static add<T>(arr: T[], ...values: T[]) {
			for (let i = 0; i < values.length; i++) {
				let value = values[i]
				if (this.has(arr, value))
					continue
				arr.push(value)
			}
		}

		/**
		 * 随机一个自身的值
		 * @param value 
		 * @param exclude  ture 排除
		 */
		public static random<T>(value: T[], exclude?: (value: T, index: number) => boolean, random?: () => number): T {
			if (value.length == 0)
				return null!


			// 从排除的数据中查找
			if (exclude) {
				let values: T[] = []
				for (let i = 0, l = value.length; i < l; i++)
					if (!exclude(value[i], i))
						values.push(value[i])

				return this.random(values)
			}

			return value[Maths.zeroToMax(value.length, random ? random() : Math.random())]
		}

		/**
	 * 随机一个自身的值
	 * @param value 
	 * @param exclude 排除
	 */
		public static randomOf<T>(value: T[], exclude?: (value: T, index: number) => boolean): number {
			if (value.length == 0)
				return null!

			// 从排除的数据中查找
			if (exclude) {
				let values: T[] = []
				for (let i = 0, l = value.length; i < l; i++)
					if (!exclude(value[i], i))
						values.push(value[i])

				return this.randomOf(values)
			}

			return Maths.zeroToMax(value.length)
		}

		/**获取值如index超出个数 则取最后一个 */
		public static getAt<T>(value: T[], index: number): T {
			index = Maths.clampf(index, 0, value.length - 1)
			return value[index]
		}



		/**
		 * 筛选 cb => true 的值 如没有 返回 null!
		 * @param {(value: T, index: number) => boolean} cb
		 * @returns {T}
		 * @memberof Sets
		 */
		public static filterValue<T>(values: T[], cb: (value: T, index: number) => boolean, isLast: boolean = false): T {
			let idx = this.filterOf(values, cb, isLast)
			if (idx == -1)
				return null!
			return values[idx]
		}

		/**
		 * 插入值
		 * @param values 
		 * @param insertIndex 
		 * @param getIndex 
		 */
		public static insert<T>(values: T[], value: T, index: number): T[] {
			let back = values.splice(index)
			values.push(value)
			values.push(...back)
			return values
		}

		/**
		 * 筛选 cb => true 的索引
		 * @param {(value: T, index: number) => boolean} cb
		 * @returns {number}
		 * @memberof Sets
		 */
		public static filterOf<T>(arr: T[], cb: (value: T, index: number) => boolean, isLast: boolean = false): number {
			if (isLast) {
				for (let i = arr.length - 1; i >= 0; i--)
					if (arr[i] && cb(arr[i], i))
						return i
			}
			else {
				for (let i = 0; i < arr.length; i++)
					if (arr[i] && cb(arr[i], i))
						return i
			}
			return -1
		}

		public static filter<T>(values: T[], cb: (value: T, index: number) => boolean, out?: T[]): T[] {
			if (!out)
				out = []
			for (let i = 0; i < values.length; i++) {
				if (values[i] && cb(values[i], i)) {
					out.push(values[i])
				}
			}
			return out
		}


		public static push<T>(values: T[], push: (T[]) | T): T[] {
			if (Array.isArray(push))
				for (let value of push)
					values.push(value)
			else
				values.push(push)
			return values
		}


		/**越大的越在前 */
		public static sortMax<T>(values: T[], cb: (value: T) => number) {
			return values.sort((a, b) => cb(b) - cb(a))
		}

		/**越小的越在前 */
		public static sortMin<T>(values: T[], cb: (value: T) => number) {
			return values.sort((a, b) => cb(a) - cb(b))
		}

		/**
		 * 打乱数组, 默认改变
		 * @param {boolean} [isChangeOriginal=true] 是否改变原数组
		 * @returns {any[]}
		 */
		public static shuffle<T>(arr: T[], isChangeOriginal: boolean = true, random?: () => number): T[] {
			if (!random)
				random = Math.random

			if (!isChangeOriginal) {
				let tmp = arr.concat()

				tmp.sort((a, b) => {
					return random!() > .5 ? 1 : -1
				})
				return tmp
			} else {
				arr.sort((a, b) => {
					return random!() > .5 ? 1 : -1
				})

				return arr
			}
		}

		/**
		 * 随机取count个数据
		 * @param vlaues 
		 * @param count 
		 */
		public static randomArray<T>(values: T[], count: number = 1): T[] {
			let res = this.shuffle(values, false),
				arr = []
			for (let i = 0; i < count; i++)
				if (res[i])
					arr.push(res[i])
			return arr
		}

		public values: T[] = []

		/**是否对数组进行去重处理 */
		private isRepeat: boolean = true

		/**
		 * 
		 * @param {boolean} [isRepeat=true] 去重
		 * @param {...T[]} values
		 * @memberof Sets
		 */
		constructor(isRepeat: boolean = true, ...values: T[]) {
			this.isRepeat = isRepeat
			this.add(...values)
		}

		public get size(): number {
			return this.values.length
		}

		public indexOf(value: T): number {
			return this.values.indexOf(value)
		}


		public clear() {
			this.values.length = 0;
		};

		/**
		 * 拿下标第一个的值 默认删除第一个
		 * @param {boolean} [isOrign=true] 是否删除第一个
		 * @returns {T}
		 * @memberof Sets
		 */
		public shift(isOrign: boolean = true): T {
			if (isOrign)
				return this.values.shift() as T
			else
				return this.values.concat()[0]
		}
		public toString(): string { return this.values.toString() }
		public forEach(cb: (value: T, index: number) => (boolean | void), isLast: boolean = false): Sets<T> { Sets.forEach(this.values, cb, isLast); return this }
		public add(...values: T[]): Sets<T> { Sets.add(this.values, ...values); return this }
		public delete(value: T): boolean { return Sets.delete(this.values, value) }
		public has(value: T, stringCompare: boolean = false): boolean { return Sets.has(this.values, value, stringCompare) }
		public map<U>(cb: (value: T, index: number, array: T[]) => U): U[] { return Sets.map(this.values, cb) }
		public filterOf(cb: (value: T, index: number) => boolean): number { return Sets.filterOf(this.values, cb) }
		public filterValue(cb: (value: T, index: number) => boolean): T { return Sets.filterValue(this.values, cb) }
		public filter(cb: (value: T, index: number) => boolean): T[] { return Sets.filter(this.values, cb, []) }

	}
}

